﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Collections.Specialized;

namespace blackhouse.WebNemo.Data {
    public abstract class AdoDataAccess : IDataAccess {

        #region IGroupDataAccess

        public abstract void SaveGroup(int id, string displayName, string identyficatorFormat, bool supportTransate, bool readOnly);

        public abstract void DeleteGroup(int id);

        public abstract int CreateGroup(string systemName);

        public GroupData[] GetAllGroups() {
            return this.ReadGroups(this.GetReaderForAllGroups());
        }

        public GroupData GetGroup(int groupId) {
            return this.ReadGroup(this.GetReaderForGroup(groupId));
        }

        public abstract bool CheckGroupName(string checkName);

        public abstract DataTable GetGroupScheme(FromGroup[] groups);

        public GroupColumnData[] GetColumns(int groupId) {
            return this.ReadGroupColumns(this.GetReaderForGroupColumns(groupId));
        }

        public GroupRecordData GetGroupRecord(SelectColumn[] selectColumns, int recordId, FromGroup[] groups, string language) {
            return this.ReadGroupRecord(selectColumns, this.GetReaderForGroupRecord(selectColumns, recordId, groups, language), groups.Any(fg => fg.GroupType != "View"), this.GetSelectColumnsIfMoreOneGroups(groups));
        }

        public GroupRecordData[] GetGroupRecords(SelectColumn[] selectColumns, WhereColumn[] whereColumns, FromGroup[] groups, string language) {
            return this.ReadGroupRecords(selectColumns, this.GetReaderForGroupRecords(selectColumns, whereColumns, groups, language), groups.Any(fg => fg.GroupType != "View"), this.GetSelectColumnsIfMoreOneGroups(groups));
        }

        public GroupRecordData[] GetGroupRecords(SelectColumn[] selectColumns, ProcedureQuery procedure, FromGroup[] groups, string language) {
            return this.ReadGroupRecords(selectColumns, this.GetReaderForGroupRecords(selectColumns, procedure), groups.Any(fg => fg.GroupType != "View"), this.GetSelectColumnsIfMoreOneGroups(groups));
        }

        //public GroupRecordData[] GetRelatedGroupRecords(SelectColumn[] selectColumns, int groupId, string columnName, int relGroupId, int relRecordId, string language, string groupType) {
        //    return this.ReadGroupRecords(selectColumns, this.GetReaderForRelatedGroupRecords(selectColumns, groupId, columnName, relGroupId, relRecordId, language, groupType), groupType != "View");
        //}

        public GroupRecordData[] GetRelatedGroupRecords(SelectColumn[] columns, FromGroup[] groups, WhereColumn[] whereClauses, string language) {
            return this.ReadGroupRecords(columns, this.GetReaderForRelatedGroupRecords(columns, groups, whereClauses, language), groups.Any(fg => String.IsNullOrEmpty(fg.Relation.ColumnName) && fg.GroupType != "View"));
            throw new NotImplementedException();
        }

        private string[] GetSelectColumnsIfMoreOneGroups(FromGroup[] groups) {
            if (groups.Length > 1)
                return groups.Where(g => !String.IsNullOrEmpty(g.Relation.ParentTableName)).Select(g => g.TableName.ToLower() + "_idx").ToArray();
            else
                return new string[0];
        }

        public NemoUserData[] GetNemoUsers() {
            return this.ReadUserRecords(this.GetReaderForNemoUsers());
        }

        public KeyValuePair<int, string>[] GetGroupSystemNames() {
            return this.ReadGroupSystemNames(this.GetReaderForGroupSystemNames());
        }

        public string[] GetAvalibleLanguages() {
            List<string> sc = new List<string>();
            IDataReader reader = this.GetReaderForAvalibleLanguages();
            while (reader.Read()) {
                sc.Add(Convert.ToString(reader[0]));
            }
            reader.Close();
            return sc.ToArray();
        }

        public TabData[] GetTabsForGroup(int groupId) {
            return this.ReadTabDatas(this.GetReaderForGroupTabs(groupId));
        }

        public SplitColumnsData[] GetColumnsSplit(int groupId) {
            return this.ReadColumnsSplits(this.GetReaderForColumnsSplit(groupId));
        }

        public abstract string GetTranslate(int groupId, int fieldId, int recordId, string language);

        public abstract string GetOryginalTranslate(int groupId, int fieldId, int recordId);

        public abstract void SetTranslate(int groupId, int fieldId, int recordId, string language, string value, int userId);

        public abstract void RemoveTranslate(int groupId, int fieldId, int recordId, string language);

        public abstract string GetCustomEditControlConfiguration(int columnId);

        #endregion

        #region Methods

        private SplitColumnsData ReadColumnsSplit(IDataReader reader) {
            SplitColumnsData data = default(SplitColumnsData);
            if (reader.Read()) {
                data = this.ReadColumnsSplitFields(reader);
            }
            reader.Close();
            return data;
        }

        private SplitColumnsData[] ReadColumnsSplits(IDataReader reader) {
            List<SplitColumnsData> data = new List<SplitColumnsData>();
            while (reader.Read()) {
                data.Add(this.ReadColumnsSplitFields(reader));
            }
            reader.Close();
            return data.ToArray();
        }

        private SplitColumnsData ReadColumnsSplitFields(IDataReader reader) {
            SplitColumnsData data = new SplitColumnsData();
            data.TabId = Convert.ToInt32(reader["TabId"]);
            data.ColumnId = Convert.ToInt32(reader["ColumnId"]);
            return data;
        }

        private TabData ReadTabData(IDataReader reader) {
            TabData data = default(TabData);
            if (reader.Read()) {
                data = this.ReadTabDataFields(reader);
            }
            reader.Close();
            return data;
        }

        private TabData[] ReadTabDatas(IDataReader reader) {
            List<TabData> data = new List<TabData>();
            while (reader.Read()) {
                data.Add(this.ReadTabDataFields(reader));
            }
            reader.Close();
            return data.ToArray();
        }

        private TabData ReadTabDataFields(IDataReader reader) {
            TabData data = new TabData();
            data.TabId = Convert.ToInt32(reader["TabId"]);
            data.TabName = Convert.ToString(reader["TabName"]);
            data.Sort = Convert.ToInt32(reader["Sort"]);
            return data;
        }

        private KeyValuePair<int, string>[] ReadGroupSystemNames(IDataReader reader) {
            List<KeyValuePair<int, string>> data = new List<KeyValuePair<int, string>>();
            while (reader.Read())
                data.Add(new KeyValuePair<int, string>(Convert.ToInt32(reader["GroupId"]), Convert.ToString(reader["SystemName"])));
            reader.Close();
            return data.ToArray();
        }

        private Dictionary<int, string[]> ReadGroupExtensions(IDataReader reader) {
            Dictionary<int, string[]> data = new Dictionary<int, string[]>();
            while (reader.Read()) {
                this.ReadGroupExtensionFields(reader, data);
            }
            reader.Close();
            return data;
        }

        private void ReadGroupExtensionFields(IDataReader reader, Dictionary<int, string[]> data) {
            int groupId = Convert.ToInt32(reader["GroupId"]);
            string recordExtension = string.Empty;
            string groupExtension = string.Empty;
            if (!reader.IsDBNull(reader.GetOrdinal("RecordExtension")))
                recordExtension = Convert.ToString(reader["RecordExtension"]);
            if (!reader.IsDBNull(reader.GetOrdinal("GroupExtension")))
                groupExtension = Convert.ToString(reader["GroupExtension"]);
            data.Add(groupId, new string[] { recordExtension, groupExtension });
        }

        private NemoUserData ReadUserRecord(IDataReader reader) {
            NemoUserData data = default(NemoUserData);
            if (reader.Read()) {
                data = this.ReadUserRecordFields(reader);
            }
            reader.Close();
            return data;
        }

        private NemoUserData[] ReadUserRecords(IDataReader reader) {
            List<NemoUserData> data = new List<NemoUserData>();
            while (reader.Read()) {
                data.Add(this.ReadUserRecordFields(reader));
            }
            reader.Close();
            return data.ToArray();
        }

        private NemoUserData ReadUserRecordFields(IDataReader reader) {
            NemoUserData data = new NemoUserData();
            data.Id = Convert.ToInt32(reader["UserId"]);
            data.Name = Convert.ToString(reader["Name"]);
            return data;
        }

        private GroupRecordData ReadGroupRecord(SelectColumn[] columns, IDataReader reader, bool readTimes, params string[] specialColumnsRead) {
            GroupRecordData data = null;
            if (reader.Read()) {
                data = this.ReadGroupRecordFields(columns, reader, readTimes, specialColumnsRead);
            }
            reader.Close();
            return data;
        }

        private GroupRecordData[] ReadGroupRecords(SelectColumn[] columns, IDataReader reader, bool readTimes, params string[] specialColumnsRead) {
            List<GroupRecordData> data = new List<GroupRecordData>();
            while (reader.Read()) {
                data.Add(this.ReadGroupRecordFields(columns, reader, readTimes, specialColumnsRead));
            }
            reader.Close();
            return data.ToArray();
        }

        private GroupRecordData ReadGroupRecordFields(SelectColumn[] columns, IDataReader reader, bool readTimes, params string[] specialColumnsRead) {
            GroupRecordData data = new GroupRecordData();
            foreach (SelectColumn column in columns) {
                data.Add(column.ColumnName, reader[column.ColumnName]);
            }
            data.Idx = Convert.ToInt32(reader["idx"]);
            if (!reader.IsDBNull(reader.GetOrdinal("identyfikator")))
                data.Identyficator = Convert.ToString(reader["identyfikator"]);

            if (readTimes) {
                if (!reader.IsDBNull(reader.GetOrdinal("cuser")))
                    data.CUser = Convert.ToInt32(reader["cuser"]);

                if (!reader.IsDBNull(reader.GetOrdinal("ctime")))
                    data.CTime = Convert.ToDateTime(reader["ctime"]);
                else
                    data.CTime = DateTime.MinValue;

                if (!reader.IsDBNull(reader.GetOrdinal("muser")))
                    data.MUser = Convert.ToInt32(reader["muser"]);
                if (!reader.IsDBNull(reader.GetOrdinal("mtime")))
                    data.MTime = Convert.ToDateTime(reader["mtime"]);
            }
            foreach (string specialColumn in specialColumnsRead) {
                if (reader.IsDBNull(reader.GetOrdinal(specialColumn)))
                    data.Add(specialColumn, DBNull.Value);
                else
                    data.Add(specialColumn, reader[specialColumn]);
            }
            return data;
        }

        private GroupData ReadGroup(IDataReader reader) {
            GroupData data = default(GroupData);
            if (reader.Read())
                data = this.ReadGroupFields(reader);

            reader.Close();
            return data;
        }

        private GroupData[] ReadGroups(IDataReader reader) {
            List<GroupData> data = new List<GroupData>();
            while (reader.Read()) {
                data.Add(this.ReadGroupFields(reader));
            }
            reader.Close();
            return data.ToArray();
        }

        private GroupData ReadGroupFields(IDataReader reader) {
            GroupData data = new GroupData();
            data.Id = Convert.ToInt32(reader["Id"]);
            if (!reader.IsDBNull(reader.GetOrdinal("ParentId")))
                data.ParentId = Convert.ToInt32(reader["ParentId"]);
            data.Name = Convert.ToString(reader["Name"]);
            if (!reader.IsDBNull(reader.GetOrdinal("SystemName")))
                data.SystemName = Convert.ToString(reader["SystemName"]);
            if (!reader.IsDBNull(reader.GetOrdinal("Identyficator")))
                data.Identyficator = Convert.ToString(reader["Identyficator"]);
            data.SelectTable = Convert.ToString(reader["SelectTable"]);
            data.Type = Convert.ToString(reader["Type"]);
            data.EnableTranslate = Convert.ToBoolean(reader["EnableTranslate"]);
            if (!reader.IsDBNull(reader.GetOrdinal("ReadOnly")))
                data.ReadOnly = Convert.ToBoolean(reader["ReadOnly"]);
            return data;
        }

        private GroupColumnData ReadGroupColumn(IDataReader reader) {
            GroupColumnData data = default(GroupColumnData);
            if (reader.Read())
                data = this.ReadGroupColumnFields(reader);

            reader.Close();
            return data;
        }

        private GroupColumnData[] ReadGroupColumns(IDataReader reader) {
            List<GroupColumnData> data = new List<GroupColumnData>();
            while (reader.Read()) {
                data.Add(this.ReadGroupColumnFields(reader));
            }
            reader.Close();
            return data.ToArray();
        }

        private GroupColumnData ReadGroupColumnFields(IDataReader reader) {
            GroupColumnData data = new GroupColumnData();
            data.ColumnId = Convert.ToInt32(reader["ColumnId"]);
            data.ColumnName = Convert.ToString(reader["ColumnName"]);
            data.ColumnType = Convert.ToString(reader["ColumnType"]);
            data.RelatedGroupId = Convert.ToInt32(reader["RelatedGroupId"]);
            data.Name = Convert.ToString(reader["DisplayName"]);
            data.FormattingValue = Convert.ToString(reader["FormattingValue"]);
            data.ColumnSort = Convert.ToInt32(reader["ColumnSort"]);
            if (!reader.IsDBNull(reader.GetOrdinal("RegexValidation")))
                data.RegexValidation = Convert.ToString(reader["RegexValidation"]);
            if (!reader.IsDBNull(reader.GetOrdinal("Description")))
                data.Description = Convert.ToString(reader["Description"]);
            if (!reader.IsDBNull(reader.GetOrdinal("CustomEditControl")))
                data.CustomEditControl = Convert.ToString(reader["CustomEditControl"]);
            data.HideForEdition = Convert.ToBoolean(reader["HideForEdition"]);
            data.Editable = Convert.ToBoolean(reader["Editable"]);
            data.GroupId = Convert.ToInt32(reader["BelongsToGroupId"]);
            return data;
        }

        public CustomValidatorData[] GetColumnValueValidators(int columnId) {
            return this.ReadColumnValidators(this.GetReaderForColumnValueValidators(columnId));
        }

        public Dictionary<int, string> GetFileGuids(string tableName, string columnName) {
            return this.ReadFileGuids(this.GetReaderFileGuids(tableName, columnName));

        }

        public abstract byte[] GetFileStream(string columnName, string tableName, int recordId);

        public abstract byte[] GetFileStream(string columnName, int tableId, int recordId);

        public FileData GetFileInfo(string columnName, int tableId, int recordId) {
            return this.ReadFileInfoData(this.GetFileInfoReader(columnName, tableId, recordId));
        }

        public FileData GetFileInfo(string columnName, string tableName, int recordId) {
            return this.ReadFileInfoData(this.GetFileInfoReader(columnName, tableName, recordId));
        }

        protected abstract IDataReader GetFileInfoReader(string columnName, string tableName, int recordId);

        protected abstract IDataReader GetFileInfoReader(string columnName, int tableId, int recordId);

        private FileData ReadFileInfoData(IDataReader reader) {
            FileData data = null;
            if (reader.Read())
                data = this.ReadFileInfoDataFields(reader);
            reader.Close();
            return data;
        }

        private FileData ReadFileInfoDataFields(IDataReader reader) {
            FileData data = new FileData();
            data.FileName = Convert.ToString(reader["name"]);
            data.FileSize = Convert.ToInt64(reader["size"]);
            data.Hash = new Guid(Convert.ToString(reader["hash"]));
            return data;
        }

        private Dictionary<int, string> ReadFileGuids(IDataReader reader) {
            Dictionary<int, string> guids = new Dictionary<int, string>();
            while (reader.Read()) {
                guids.Add(Convert.ToInt32(reader["id"]), Convert.ToString(reader["guid"]));
            }
            reader.Close();
            return guids;
        }

        private CustomValidatorData ReadColumnValidator(IDataReader reader) {
            CustomValidatorData data = default(CustomValidatorData);
            if (reader.Read())
                data = this.ReadColumnValidatorFields(reader);

            reader.Close();
            return data;
        }

        private CustomValidatorData[] ReadColumnValidators(IDataReader reader) {
            List<CustomValidatorData> data = new List<CustomValidatorData>();
            while (reader.Read()) {
                data.Add(this.ReadColumnValidatorFields(reader));
            }
            reader.Close();
            return data.ToArray();
        }

        private CustomValidatorData ReadColumnValidatorFields(IDataReader reader) {
            CustomValidatorData data = new CustomValidatorData();
            data.ColumnId = Convert.ToInt32(reader["ColumnId"]);
            data.ValidatorId = Convert.ToInt32(reader["ValidatorId"]);
            data.InstanceClass = Convert.ToString(reader["InstanceClass"]);
            data.XmlConfiguration = Convert.ToString(reader["XmlConfiguration"]);
            return data;
        }

        public ExtensionData GetExtensions(int groupId) {
            return this.ReadExtensionData(this.GetExtensionsReader(groupId));
        }

        public abstract void SaveExtensions(int groupId, string groupExtension, string groupRecordExtension);

        private ExtensionData ReadExtensionData(IDataReader reader) {
            ExtensionData data = default(ExtensionData);
            if (reader.Read())
                data = this.ReadExtensionDataFields(reader);
            reader.Close();
            return data;
        }

        private ExtensionData ReadExtensionDataFields(IDataReader reader) {
            ExtensionData data = new ExtensionData();
            data.GroupExtension = Convert.ToString(reader["GroupExtension"]);
            data.GroupRecordExtension = Convert.ToString(reader["RecordExtension"]);
            return data;
        }

        #endregion

        #region Abstracts

        protected abstract IDataReader GetReaderForColumnValueValidators(int columnId);

        protected abstract IDataReader GetReaderForAvalibleLanguages();

        protected abstract IDataReader GetReaderForAllGroups();

        protected abstract IDataReader GetReaderForGroup(int groupId);

        protected abstract IDataReader GetReaderForGroupColumns(int groupId);

        protected abstract IDataReader GetReaderForGroupRecord(SelectColumn[] columns, int recordId, FromGroup[] groups, string language);

        protected abstract IDataReader GetReaderForGroupRecords(SelectColumn[] columns, WhereColumn[] whereColumns, FromGroup[] groups, string language);

        protected abstract IDataReader GetReaderForGroupRecords(SelectColumn[] selectColumns, ProcedureQuery procedure);

        protected abstract IDataReader GetReaderForRelatedGroupRecords(SelectColumn[] columns, FromGroup[] groups, WhereColumn[] whereClauses, string language);

        public abstract IDataReader GetReaderForNemoUsers();

        public abstract IDataReader GetReaderForGroupSystemNames();

        public abstract IDataReader GetReaderForGroupTabs(int groupId);

        public abstract IDataReader GetReaderForColumnsSplit(int groupId);

        protected abstract IDataReader GetReaderFileGuids(string tableName, string columnName);

        public abstract void ChangeIdentyficator(int groupId, int recordId, string ident, int userId);

        public abstract void RemoveRecord(int groupId, int recordId, int userId);

        public abstract void SaveGroupRecord(GroupRecordData data, int groupId, int recordId, int userId);

        public abstract int AddGroupRecord(GroupRecordData data, int groupId, int userId);

        public abstract void SaveGroupRecordFile(int groupId, int recordId, string column, string fileName, Stream stream, long streamSize, int userId);

        public abstract void RemoveRelationToGroupRecord(int relGroupId, int relRecordId, int parentGroupId, string parentColumnName, int parentRecordId);

        public abstract void AddRelationToGroupRecord(int relGroupId, int relRecordId, int parentGroupId, string parentColumnName, int parentRecordId, int userId);

        public VirtualColumnData[] GetVirtualColumns(int groupId) {
            throw new NotImplementedException();
        }

        protected abstract IDataReader GetExtensionsReader(int groupId);

        public abstract bool CheckViewDeclaration(string selectDeclaration);

        public abstract int CreateView(string systemName, string selectDeclaration);

        #endregion

    }
}
